package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.SignRecord;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mapper.SignRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.tianji.common.constants.MqConstants.Exchange.LEARNING_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.SIGN_IN;
import static com.tianji.learning.constants.RedisConstants.SIGN_RECORD_KEY_PREFIX;

/**
 * <p>
 * 签到记录表 服务实现类
 * </p>
 *
 * @author ffk
 * @since 2024-06-29
 */
@RequiredArgsConstructor
@Service
public class SignRecordServiceImpl extends ServiceImpl<SignRecordMapper, SignRecord> implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper rabbitMqHelper;

    /**
     * 签到
     */
    @Override
    public SignResultVO makeSignRecord() {
        //组装reids key，当天的积分所以需要日期，用户id
        Long userId = UserContext.getUser();
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();

        String key= SIGN_RECORD_KEY_PREFIX+userId+now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);

        //偏移量，即将这个位置处设置为true，即已登录
        int offset = dayOfMonth-1;
        //改变则返回true
        Boolean aBoolean = redisTemplate.opsForValue().setBit(key, offset, true);

        //没有改变
        if(BooleanUtils.isTrue(aBoolean)){
            throw  new BizIllegalException("今天已经签到过了");
        }


        //连续签到天数，从0 到当天日，
         int signDays = 0;
        //创建一个BitFieldSubCommands构建器，用于构建一系列位字段操作命令
        List<Long> days = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(
                BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );

        if(ObjectUtils.isNotEmpty(days)){
            //代表了多天签到的一个整数（二进制变来的）
            int num = days.get(0).intValue();
            //连续签到数目
            int count = 0;
            while ((num & 1) == 1) {
                count++;
                num >>>= 1;
            }
            signDays=count;
        }

        //积分奖励
        int rewardPoints = 0;
        switch (signDays) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        //封装
        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignDays(signDays);
        signResultVO.setRewardPoints(rewardPoints);

        // 记录积分
        try {
            rabbitMqHelper.send(
                    LEARNING_EXCHANGE,
                    SIGN_IN,
                    SignInMessage.of(userId, signResultVO.totalPoints()));//奖励加签到
        } catch (Exception e) {
            log.error("发送签到积分消息失败:{}", e);
        }

        return signResultVO;
    }

    /**
     * 获取签到记录01数组
     * @return
     */
    @Override
    public Byte[] getSignDays() {
        Long userId = UserContext.getUser();
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        String key= SIGN_RECORD_KEY_PREFIX+userId+now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);

        List<Long> days = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(
                BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        if(ObjectUtils.isEmpty(days)){
            return new Byte[]{};
        }

//        List<Integer> signDaysList=new ArrayList<>(dayOfMonth);

        Byte[] arr = new Byte[dayOfMonth];

        //下标
        int index=dayOfMonth-1;
        int num = days.get(0).intValue();

            while (index>=0) {
                 int day = num & 1;
                 arr[index]=(byte)day;
//                signDaysList.add(day);
                index--;
                num >>>= 1;
            }

        return arr;
    }
}
